/**
 * @author salim & sidyous
 */

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

public class Server extends UnicastRemoteObject implements Server_itf {

	/*
	 * Attributes
	 */

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private HashMap<String, Integer> namingServer;
	private Vector<ServerObject> serverObjectCollection;
	private AtomicInteger nextFreePosition;

	/*
	 * Methods
	 */

	/**
	 * @throws RemoteException
	 * 
	 */
	public Server() throws RemoteException {
		super();
		System.out.println("Server : Server");
		namingServer = new HashMap<String, Integer>();
		serverObjectCollection = new Vector<ServerObject>();
		nextFreePosition = new AtomicInteger(0);
	}

	/**
	 * Create the ServerObject and add it to serverBojectList
	 * 
	 * @return the ServerObject id
	 */
	@Override
	public int create(Object obj) throws RemoteException {
		System.out.println("Server : create");
		int id = nextFreePosition.getAndIncrement();
		serverObjectCollection.add(id, new ServerObject(obj, id));
		return id;
	}

	/**
	 * get the id of the ServerObject registered under the name <name>
	 * 
	 * @return the id of the ServerObject. If (id == -1), it means that the object
	 *         targeted by this name doesn't exist
	 */
	@Override
	public int lookup(String name) throws RemoteException {
		System.out.println("Server : lookup " + name);
		if (namingServer.containsKey(name))	return namingServer.get(name); else return -1;
	}
	
	/**
	 * get the sharedObject whose id is <id>
	 * 
	 * @return the SharedObject. the id must be in the sever
	 */
	@Override
	public SharedObject get(int id) throws RemoteException {
		System.out.println("Client : get " + id);
		
		Object obj = serverObjectCollection.get(id).obj;
		String className = obj.getClass().getName();
		
		Class stubClass;
		
		try {
			stubClass = Class.forName(className + "_stub");
		} catch (ClassNotFoundException e) {
			System.err.println ("Class "+className+" not found.");
			return null;
		}
		
		Class[] parameterTypes = new Class[] {Object.class, int.class};
		
		Constructor constructor = null;
		
		try {
			constructor = stubClass.getConstructor(parameterTypes);
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Object[] initArgs = new Object[] {obj, id};
		
		SharedObject so = null;
		
		try {
			so = (SharedObject)(constructor.newInstance(initArgs));
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

		return so;
		
	}

	/**
	 * register the ServerObject in the namingServer under the name <name>
	 */
	@Override
	public void register(String name, int id) throws RemoteException {
		System.out.println("Server : register " + name + " " + id);
		// XXX : danger if id is not allocated yet, or if it is even registred
		namingServer.put(name, id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Server_itf#lock_read(int, Client_itf)
	 */
	@Override
	public Object lock_read(int id, Client_itf client) throws RemoteException {
		System.out.println("Server : lock_read " + id);
		return serverObjectCollection.get(id).lock_read(client);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Server_itf#lock_write(int, Client_itf)
	 */
	@Override
	public Object lock_write(int id, Client_itf client) throws RemoteException {
		System.out.println("Server : lock_write " + id);
		return serverObjectCollection.get(id).lock_write(client);
	}

	public static void main(String[] args) {

		int port = 8081;
		String URL;
		String serverName = "remoteServer";
		// temp for dev
		String URL2 = "//localhost:" + port + "/" + serverName;
//		String URL2 = "//192.168.30.11:" + port + "/" + serverName;
		try {

			// Creation of the rmiregistry
			Registry registry = LocateRegistry.createRegistry(port);

			// Creation of the object Server
			Server server = new Server();

			// Calculation of the URL
			URL = "//" + InetAddress.getLocalHost().getHostName() + ":" + port
					+ "/" + serverName;

			// Deployment of the server
			Naming.bind(URL, server);

			System.out.println("Server deployed under: " + URL);

		} catch (Exception exc) {
			// System.out.println(exc);
			exc.printStackTrace();
		}
	}

}
